package itdep.common.context.spring;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.*;
import org.springframework.security.core.Authentication;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author Ivan Khalopik
 * @version $Revision: 4 $ $Date: 2010-06-10 13:13:16 +0000 (Thu, 10 Jun 2010) $
 */
public class AclPermissionVoter implements AccessDecisionVoter {
	private AclService aclService;
	private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
	private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();

	@Required
	public void setAclService(final AclService aclService) {
		this.aclService = aclService;
	}

	public void setObjectIdentityRetrievalStrategy(final ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
		this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
	}

	public void setSidRetrievalStrategy(final SidRetrievalStrategy sidRetrievalStrategy) {
		this.sidRetrievalStrategy = sidRetrievalStrategy;
	}

	public boolean supports(final ConfigAttribute attribute) {
		return attribute != null && PermissionAttribute.class.isAssignableFrom(attribute.getClass());
	}

	public boolean supports(final Class clazz) {
		return true;
	}

	public int vote(final Authentication authentication, final Object object, final Collection<ConfigAttribute> attributes) {
		final List<Permission> permissions = getPermission(attributes);
		if (permissions.size() > 0) {
			final ObjectIdentity identity = objectIdentityRetrievalStrategy.getObjectIdentity(object);
			final List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
			try {
				final Acl acl = aclService.readAclById(identity, sids);
				if (acl.isGranted(permissions, sids, false)) {
					return ACCESS_GRANTED;
				} else {
					return ACCESS_DENIED;
				}
			} catch (NotFoundException e) {
				return ACCESS_DENIED;
			}
		}
		return ACCESS_ABSTAIN;
	}

	private List<Permission> getPermission(final Collection<ConfigAttribute> attributes) {
		final List<Permission> permissions = new ArrayList<Permission>();
		for (ConfigAttribute configAttribute : attributes) {
			if (supports(configAttribute)) {
				permissions.add(((PermissionAttribute) configAttribute).getPermission());
			}
		}
		return permissions;
	}
}
